#include "i2c.h"
#include "rcc.h"


/** @defgroup I2C_Private_Functions
  * @{
  */

/**
  * @brief  Deinitializes the I2Cx peripheral registers to their default reset values.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  * @retval None
  */
void I2C_DeInit(I2C_TypeDef* I2Cx)
{
  if (I2Cx == I2C)
  {
    /* Enable I2C reset state */
    RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C, ENABLE);
    /* Release I2C from reset state */
    RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C, DISABLE);
  }

}

/**
  * @brief  Initializes the I2Cx peripheral according to the specified 
  *   parameters in the I2C_InitStruct.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  * @param  I2C_InitStruct: pointer to a I2C_InitTypeDef structure that
  *   contains the configuration information for the specified I2C peripheral.
  * @retval None
  */
void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct)
{

  uint16_t i2cccr_reg = 0;
  uint8_t i2c_prescaler = 0,i2c_prescaler_tmp=0, i=0,j=0;
  uint32_t pclk1 = 16000000,freqrange = 0,i2c_speed = 0,i2c_speed_fac = 0;
	uint32_t error_cnt = 0,error_mix = 0xFFFFFFFF;
  RCC_ClocksTypeDef  rcc_clocks;
	

	
  RCC_GetClocksFreq(&rcc_clocks);
  pclk1 = rcc_clocks.PCLK1_Frequency;
  /* Set frequency bits depending on pclk1 value */
  freqrange = pclk1;	
	
	i2cccr_reg = I2Cx->CCR;


	
	i2c_prescaler = I2C_InitStruct->I2C_Prescaler;
	i2c_speed = I2C_InitStruct->I2C_ClockSpeed;
	if((i2c_speed) >=100000)
	{
		i2cccr_reg |= I2C_CR_HSP_Set;
	}
	
	i2cccr_reg |= I2C_InitStruct->I2C_Mode; 
	
	if(I2C_InitStruct->I2C_Ack != DISABLE)
	{
		i2cccr_reg |= I2C_CR_ACK_Set;
	}
	
	for(i=7;i<0x1F;i++)
	{
		for(j=2;j<8;j++)
		{
			error_cnt = freqrange - ((2<<(j-1))*(i+1)*i2c_speed);
			if(error_cnt <= error_mix)
			{
				error_mix = error_cnt;
				i2c_prescaler_tmp = j;
				i2c_speed_fac = i+1;
			}
		}
	}
	i2c_prescaler = i2c_prescaler_tmp;

	i2cccr_reg &= ~0xFF00;
	i2cccr_reg |= (uint16_t)((i2c_prescaler<<5|i2c_speed_fac)<<8);
	

	
	I2Cx->CCR = (uint16_t)(i2cccr_reg);

}

/**
  * @brief  Fills each I2C_InitStruct member with its default value.
  * @param  I2C_InitStruct: pointer to an I2C_InitTypeDef structure which will be initialized.
  * @retval None
  */
void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct)
{
/*---------------- Reset I2C init structure parameters values ----------------*/
	I2C_InitStruct->I2C_Prescaler = 0;
  /* initialize the I2C_ClockSpeed member */
  I2C_InitStruct->I2C_ClockSpeed = 10000;
  /* Initialize the I2C_Mode member */
  I2C_InitStruct->I2C_Mode = I2C_Mode_I2C;
  /* Initialize the I2C_Ack member */
  I2C_InitStruct->I2C_Ack = I2C_Ack_Enable;
}

/**
  * @brief  Enables or disables the specified I2C peripheral.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  * @param  NewState: new state of the I2Cx peripheral. 
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState)
{
	uint16_t i2cccr_reg = 0;
	
  i2cccr_reg = I2Cx->CCR;
	
  if (NewState != DISABLE)
  {
    /* Enable the selected I2C peripheral */
		i2cccr_reg |= I2C_CR_EN_Set;
  }
  else
  {
    /* Disable the selected I2C peripheral */
    i2cccr_reg &= I2C_CR_EN_Reset;
  }
	
	I2Cx->CCR = i2cccr_reg;
}



/**
  * @brief  Generates I2Cx communication START condition.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  * @param  NewState: new state of the I2C START condition generation.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None.
  */
void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState)
{

  
	uint16_t i2cccr_reg = 0;
	
  i2cccr_reg = I2Cx->CCR;
	
  if (NewState != DISABLE)
  {
    /* Enable the selected I2C peripheral */
		i2cccr_reg |= I2C_CR_START_Set;
  }
  else
  {
    /* Disable the selected I2C peripheral */
    i2cccr_reg &= I2C_CR_START_Reset;
  }
	
	I2Cx->CCR = i2cccr_reg;
}

/**
  * @brief  Generates I2Cx communication STOP condition.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  * @param  NewState: new state of the I2C STOP condition generation.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None.
  */
void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState)
{
	uint16_t i2cccr_reg = 0;
	
  i2cccr_reg = I2Cx->CCR;
	
  if (NewState != DISABLE)
  {
    /* Enable the selected I2C peripheral */
		i2cccr_reg |= I2C_CR_STOP_Set;
  }
  else
  {
    /* Disable the selected I2C peripheral */
    i2cccr_reg &= I2C_CR_STOP_Reset;
  }
	
	I2Cx->CCR = i2cccr_reg;
}

/**
  * @brief  Enables or disables the specified I2C acknowledge feature.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  * @param  NewState: new state of the I2C Acknowledgement.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None.
  */
void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState)
{
	uint16_t i2cccr_reg = 0;
	
  i2cccr_reg = I2Cx->CCR;
	
  if (NewState != DISABLE)
  {
    /* Enable the selected I2C peripheral */
		i2cccr_reg |= I2C_CR_ACK_Set;
  }
  else
  {
    /* Disable the selected I2C peripheral */
    i2cccr_reg &= I2C_CR_ACK_Reset;
  }
	
	I2Cx->CCR = i2cccr_reg;
}

/**
  * @brief  Enables or disables the specified I2C AddressMask.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  *   This parameter can be: AddressMask.
  * @retval None
  */
void I2C_SlaveAddressMaskConfig(I2C_TypeDef* I2Cx, uint8_t AddressMask)
{
	uint16_t i2cadr_reg = 0;
	
	i2cadr_reg = I2Cx->ADR;

	i2cadr_reg &= ~I2C_ADR_ADDR_MASK_MASK;
	
	i2cadr_reg |= (AddressMask<<8)&I2C_ADR_ADDR_MASK_MASK;
	
	I2Cx->ADR = i2cadr_reg;
		
}
/**
  * @brief  Enables or disables the specified I2C Address.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  *   This parameter can be: Address.
  * @retval None
  */
void I2C_SlaveAddressConfig(I2C_TypeDef* I2Cx, uint8_t Address)
{
	uint16_t i2cadr_reg = 0;
	
	i2cadr_reg = I2Cx->ADR;

	i2cadr_reg &= ~I2C_ADR_ADDR_MASK;
	
	i2cadr_reg |= (Address>>1)&I2C_ADR_ADDR_MASK;
	
	I2Cx->ADR = i2cadr_reg;	
}
/**
  * @brief  Enables or disables the specified I2C GCE.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void I2C_GCECmd(I2C_TypeDef* I2Cx,FunctionalState NewState)
{
	uint16_t i2cadr_reg = 0;
	
	i2cadr_reg = I2Cx->ADR;

	if(NewState != DISABLE)
	{
		i2cadr_reg |= I2C_ADR_GCE_Set;
	}
	else
	{
		i2cadr_reg &= I2C_ADR_GCE_Clean;
	}
	I2Cx->ADR = i2cadr_reg;	
}
/**
  * @brief  Enables or disables the specified I2C SPFE.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void I2C_SPFECmd(I2C_TypeDef* I2Cx,FunctionalState NewState)
{
	uint16_t i2cadr_reg = 0;
	
	i2cadr_reg = I2Cx->ADR;

	if(NewState != DISABLE)
	{
		i2cadr_reg |= I2C_ADR_SPFE_Set;
	}
	else
	{
		i2cadr_reg &= I2C_ADR_SPFE_Clean;
	}
	I2Cx->ADR = i2cadr_reg;		
}

/**
  * @brief  Enables or disables the specified I2C interrupts.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void I2C_ITConfig(I2C_TypeDef* I2Cx, FunctionalState NewState)
{

	uint16_t i2cccr_reg = 0;
	
  i2cccr_reg = I2Cx->CCR;
	
  if (NewState != DISABLE)
  {
    /* Enable the selected I2C peripheral */
		i2cccr_reg |= I2C_CR_IT_Set;
  }
  else
  {
    /* Disable the selected I2C peripheral */
    i2cccr_reg &= I2C_CR_IT_Reset;
  }
	
	I2Cx->CCR = i2cccr_reg;
}

/**
  * @brief  Sends a data byte through the I2Cx peripheral.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  * @param  Data: Byte to be transmitted..
  * @retval None
  */
void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data)
{

  /* Write in the DR register the data to be sent */
  I2Cx->DAT = Data;
}

/**
  * @brief  Returns the most recent received data by the I2Cx peripheral.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  * @retval The value of the received data.
  */
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx)
{
  return (uint8_t)I2Cx->DAT;
}

/**
  * @brief  Transmits the address byte to select the slave device.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  * @param  Address: specifies the slave address which will be transmitted
  * @param  I2C_Direction: specifies whether the I2C device will be a
  *   Transmitter or a Receiver. This parameter can be one of the following values
  *     @arg I2C_Direction_Transmitter: Transmitter mode
  *     @arg I2C_Direction_Receiver: Receiver mode
  * @retval None.
  */
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction)
{
  /* Test on the direction to set/reset the read/write bit */
  if (I2C_Direction != I2C_Direction_Transmitter)
  {
    /* Set the address bit0 for read */
    Address |= I2C_Direction_Receiver;
  }
  else
  {
    /* Reset the address bit0 for write */
    Address &= ~I2C_Direction_Receiver;
  }
  /* Send the address */
  I2Cx->DAT = Address;
}


/**
  * @brief  Checks whether the specified I2C flag is set or not.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @param  I2C_FLAG: specifies the flag to check. 
  *   This parameter can be one of the following values:
  *     @arg I2C_SR_MS_CSTART_Flag			
  *      
  *     @arg I2C_SR_M_SW_RACK_Flag			
  *     
  *     @arg I2C_SR_M_SW_NACK_Flag			
  *      
  *     @arg I2C_SR_M_BYTEF_CACK_Flag	
  *      
  *     @arg I2C_SR_M_BYTEF_NACK_Flag	
  *      													
  *     @arg I2C_SR_M_LOST_Flag				
  *      													
  *     @arg I2C_SR_M_SR_RACK_Flag			
  *      													
  *     @arg I2C_SR_M_SR_NACK_Flag			
  *     													
	*     @arg I2C_SR_RW_SACK_Flag				
	*          										
	*     @arg I2C_SR_MS_UDP_SACK_Flag		
	*          										
	*     @arg I2C_SR_S_SBYTE_CACK_Flag	
	*          											
	*     @arg I2C_SR_S_SBYTE_NACK_Flag	
	*          										
	*     @arg I2C_SR_MS_CSTOP_Flag			
	*          
	*     @arg I2C_SR_S_RR_SACK_Flag			
	*          
	*     @arg I2C_SR_MS_BUS_IDLE_Flag		
  * @retval The new state of I2C_FLAG (SET or RESET).
  */
FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint8_t I2C_FLAG)
{

	FlagStatus bitstatus = RESET;
	uint8_t i2cflg_reg = 0;
  
	i2cflg_reg = (uint8_t)(I2Cx->FLG&I2C_SR_Mask);
  if((i2cflg_reg & I2C_FLAG) != (uint32_t)RESET)
  {
    /* I2C_FLAG is set */
    bitstatus = SET;
  }
  else
  {
    /* I2C_FLAG is reset */
    bitstatus = RESET;
  }
  
  /* Return the I2C_FLAG status */
  return  bitstatus;
}

/**
  * @brief  Checks whether the specified I2C flag has occurred or not.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  * @retval The new state value of i2c Status(include interrupt).
  */
uint16_t I2C_GetBusStatus(I2C_TypeDef* I2Cx)
{
	uint16_t i2cflg_reg = 0;
	i2cflg_reg = (uint16_t)(I2Cx->FLG&I2C_SR_BUS_Flag);
	return  i2cflg_reg;	
}
/**
  * @brief  Checks whether the specified I2C flag has occurred or not.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  * @retval The new state value of i2c Status.
  */
uint8_t I2C_GetStatus(I2C_TypeDef* I2Cx)
{
	uint8_t i2cflg_reg = 0;
	i2cflg_reg = (uint8_t)(I2Cx->FLG&I2C_SR_Mask);
	return  i2cflg_reg;
}



/**
  * @brief  Checks whether the specified I2C interrupt has occurred or not.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  * @retval The new state of I2C bus (SET or RESET).
  */
FlagStatus I2C_GetBusFlagStatus(I2C_TypeDef* I2Cx)
{
  ITStatus bitstatus = RESET;


  /* Check the status of the specified I2C flag */
  if (((I2Cx->FLG & I2C_SR_BUS_Flag) != (uint32_t)RESET) )
  {
    /* I2C_IT is set */
    bitstatus = SET;
  }
  else
  {
    /* I2C_IT is reset */
    bitstatus = RESET;
  }
  /* Return the I2C_IT status */
  return  bitstatus;	
}


/**
  * @brief  Clears the I2Cx's pending flags.
  * @param  I2Cx: where x can be 1 or 2 to select the I2C peripheral.
  * @retval None
  */
void I2C_ClearFlag(I2C_TypeDef* I2Cx)
{
	
	I2Cx->FLG |= I2C_SR_BUS_Flag;
}

/**
  * @brief  Checks whether the specified I2C interrupt has occurred or not.
  * @param  I2Cx: where x can be 1 to select the I2C peripheral.
  * @retval The new state of I2C_IT (SET or RESET).
  */
ITStatus I2C_GetITStatus(I2C_TypeDef* I2Cx )
{
  ITStatus bitstatus = RESET;


  /* Check the status of the specified I2C flag */
  if (((I2Cx->FLG & I2C_SR_BUS_Flag) != (uint32_t)RESET) )
  {
    /* I2C_IT is set */
    bitstatus = SET;
  }
  else
  {
    /* I2C_IT is reset */
    bitstatus = RESET;
  }
  /* Return the I2C_IT status */
  return  bitstatus;

}

/**
  * @brief  Clears the I2Cx interrupt pending bits.
  * @param  I2Cx: where x can be 1  to select the I2C peripheral.
  * @retval None
  */
void I2C_ClearITPendingBit(I2C_TypeDef* I2Cx )
{

		I2Cx->FLG |= I2C_SR_BUS_Flag;
}
